Android Activity启动流程
Activity是四大组件之一,其启动流程分为两种:
- 显式启动 :必须实现知道类名
- 隐式启动 :只需要知道组件名称即可
Android的任务栈
了解任务和返回堆栈 | Android 开发者 | Android Developers
用户一系列操作的Activity是按打开顺序排列在一个返回堆栈里的,返回时就会按堆栈的排列返回
在当前 Activity 启动另一个 Activity 时,新的 Activity 将被推送到堆栈顶部并获得焦点
Android上是允许多任务的,一个任务就是一个任务堆栈Task,决定新的Activity是不是开启一个新的Task是由intent参数决定的,而一般从主屏幕启动一个新的活动,都是开启一个新的Task
当新应用启动时,系统会启动该应用的任务(任务 B),该任务具有自己的Task堆栈。与该应用互动后,用户再次返回到主屏幕并选择最初启动任务 A 的应用。现在,任务 A 进入前台,其堆栈中的所有三个 Activity 都完好如初,堆栈顶部的 Activity 恢复运行。
Android也支持在一个TaskStack里面,一个Activity被多此实例化
Intent里面有一个启动flag是FLAG_ACTIVITY_NEW_TASK
如果当前启动的Activity没有Task就会启动一个新的Task,否则就把其所在的task移动到前台,然后要启动的Acitivty的onNewIntent会被调用一下
根Activity的启动流程
Launcher端startActivity
根Activity一般是在Launcher中点击APP图标启动起来的,因此我们从Launcher中分析
1 | boolean startActivity(View v, Intent intent, Object tag) { |
Launcher启动根Activity就加上了FLAG_ACTIVITY_NEW_TASK的flag
Launcher继承自Activity,这里调用的startActivity(intent)就是Activity里的
1 |
|
这里传的options是null,所以走的是else
1 | public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) { |
mParent表示当前acitivty被包含在其他Activity中,一般是Fragment才有这个情况,所以这里mParent为Null
Instrument类的说明,这里就是经由Instrument类,导致我们可以通过Instrument来监控应用程序和系统之间的交互,比如这里告知AMS启动Activity
这里传入了几个参数,第一个是源Activity的Context,传的也就是this,第二个参数是一个ApplicationThread,用来描述应用程序进程,每当系统启动一个应用程序进程时,都会在里面加载一个ActivityThread实例,
ActivityThread和ApplicationThread
这里涉及两个类ActivityThread和ApplicationThread
ApplicationThread是个Ibinder对象,
ActivityThread就是一个类,代表一个应用程序进程,而ApplicationThread是作为Binder对象,把其传给AMS,AMS就可以跟应用程序进行通信了。
ActivityThread 是包含ApplicationThread的
第三个参数mToken也是一个IBinder类型的代理对象,其指向AMS里面ActivityRecord的本地对象
ActivityRecord记录已经启动的Activity组件的运行状态,这里其实就指向Launcher的ActivityRecord
(因为AMS作为系统的Activity管理器,肯定得有个类管理Activity的信息)
第四个参数是哪个Activity执行的动作,所以还是传this
第五个参数intent就不用说了
1 | public ActivityResult execStartActivity( |
调用ActivityManager.getService就是获得AMS的代理对象
IActivityManagerSingleton是一个单例对象,get就是调用其new跟的构造函数
就是从SM那获取ACTIVITY_SERVICE,然后封装成一个继承IActivityManager接口的代理对象,
老版本还在用ActivityManagerProxy这个函数作为代理对象,新版本世界就是直接用的AIDL的Proxy和Stub实现的AMS服务
这样找startActivity就得去IActivityManager.java的Proxy里找,这里就是常规的封装参数,然后调用Transact
列举一下参数
发起intent的进程的ApplicationThread对象
发起intent的app包名
具体的intent
涉及到一个resolveType
说的是返回intent的MIMEdata的类型
intent在传递MIME data数据时,是需要指定data type来作匹配用的,看个例子
Activity的过滤器里可以设置这个东西,所以这里是得到intent设置的MIMEtype
- 一个ActivityRecord对象,指向AMS内部,是Launcher组件的详细信息
- 发起动作的Activity的mEmbeddedID
- requestCode
- flags等于0
这里调用完就来到AMS的startActivity里面
AMS端的startActivity
AMS对应的实现类是ActivityManagerServer
1 | //入口startActivity |
这里是8.0里面多出的ActivityStarter,书上的是activityStack
这个类的解释,看来其就负责解析intent和flags 来决定怎么启动activity
这俩函数,区别在于第四个参数是不是intent
1 | 147 private final ActivityManagerService mService; |
这里mSuperVisor又是干什么的
ActivityStackSupervior和ActivityStack
ActivityStackSupervior是用来管理ActivityStack的
而ActivityStack是 用来管理系统所有的Activity的,其内部维护了Activity的所有状态、特殊状态的Activity以及和Activity相关的列表数据。
supervisor里有多种ActivityStack实例
1 | 327 /** The stack containing the launcher app. Assumed to always be attached to |
mHomeStack用来存储LauncherApp的所有的Activity,
mFocusStack表示当前正在接收输入或启动下一个Activity的所有Activity
mLastFocusStack表示此前输入的所有Activity
所以其实就是ActivityStack > TaskStack > Activity
ActivityRecord记录一个Activity的所有信息,TaskRecord记录TaskStack的信息
resolveIntent
1 | //解析intent |
分析一下这里是怎么解析Intent的
这里得到PM是在LocalService里得到的,这个LocalServices在其他部分也多次分析过
其就是用一个ArrayMap维护的当前本地服务对象,直接搜索引用可以找到添加服务的地方,这段代码在PMS里
可能这里会奇怪一下,PMS加的本地服务,AMS怎么访问获得?但其实不要忘了AMS、PMS本来就是在一个进程里面。
这个Impl类就在PackageManagerService.java里面
1 |
|
这里回溯ResolveType是传进来的mimeType
可以看到这里的逻辑实际就是根据intent去PMS里面查找最匹配的组件,然后封装成ResolveInfo返回
ResolveInfo里面包含的就是ActivityInfo 、ServiceInfo这些四大组件的信息,就对上PMS在安装APK时解析的信息
Android中隐式Intent的匹配规则源码分析 - 掘金 (juejin.cn)
具体是怎么解析的,回头再分析吧
回到startActivityLocked
1 | int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent, |
这里caller传进来的是Launcher的ApplicationThread的代理对象,先通过其找到ProcessRecord·
1 | //获得传进来的ApplicationThread代理对象对应的ProcessRecord |
从寻找方法就是从pmap里遍历ProcessRecord,然后对比其thread是否等于要找的IApplicationThread代理对象
1 | //resultTo指向的是Launcher组件在AMS中的一个ActivityRecord对象 |
这里根据resultTo,也就是前面说的mToken,获得ActivityRecord,
mActivityDisplays是ActivityDispaly类,其内部维护一个mStacks,就是活动栈
所以这里就是双重循环遍历mActivityDisplays里的mStacks来查找对应的ActivityRecord
1 | //创建一个activityRecord描述即将要启动的Activity组件,即MainActivity |
ActivityRecord
它是在AMS记录一个Activity的所有信息,在启动Activity时被创建,其本身并不是IBinder对象
1 | 185 /** |
而是其包含的一个内部类是Ibinder对象
1 | 748 static class Token extends IApplicationToken.Stub { |
我们根据token来查找对应的ActivityRecord时,调用的是ActivityStack的isInStackLocked,其调用的是ActivityRecord的forTokenLocked
1 | 764 ActivityRecord isInStackLocked(IBinder token) { |
然后就来到Ibinder对象Token的静态函数,其就是获取token的成员weakActivity,也就是指向一个ActivityRecord对象
1 | 755 private static ActivityRecord tokenToActivityRecordLocked(Token token) { |
列举一下ActivityRecord里的重要的成员变量
1 | final ActivityManagerService service; // owner 对AMS的引用 |
再回头看startActivity,层数太多了
1 | private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, |
这个函数就很乱了,现在也没有分析的太明白,首先可以看到其调用了setTaskFromReuseOrCreateNewTask创建一个新的TaskRecord任务栈,因为我们Launcher启动根Activity里指定的NEW_TASK
Android深入四大组件(四)Android8.0 根Activity启动过程(前篇) - 安卓笔记侠 - 博客园 (cnblogs.com)
然后调用的就是这个resumeFocusedStackTopActivity
1 | mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions); |
1 | boolean resumeFocusedStackTopActivityLocked( |
这里我们刚创建的TaskRecord的栈顶就是我们要启动的Activity,这里获得其状态肯定不是Resumed,所以走的就是resumeTopActivityUncheckLocked
1 | boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { |
这个Inner代码也是特别长,其中只需要关注调用了startPausingLocked和startSpecificActivityLocked
startPausingLocked是向Launcher发送通知,让其进入Paused状态
1 | final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, |
ActivityRecord的成员变量是ProcessRecord,其成员变量thread就是ApplicationThread代理对象
这里调用其schedulePauseActivity,其就是向消息队列里发了一个PAUSE_ACTIVITY消息
1 | public final void schedulePauseActivity(IBinder token, boolean finished, |
来到消息队列的case
1 | case PAUSE_ACTIVITY: { |
这里不继续跟进了,描述一下过程,在应用程序中的Activity组件都用一个ActivityClientRecord来描述,这些Record对象对应AMS里面的ActivityRecord对象,保存在Activty的mActivites成员中
handlePauseActivity干了三件事
- 向Launcher发送一个用户离开事件通知,即调用其成员函数onUserLeaveHint
- 向Launcher发送一个中止事件通知,即调用其onPause
- 调用waitToFinish完成数据写入操作,
再回到前面,Launcher被Pause之后,就该启动栈顶的目标Activity了
1 | //需要暂停当前活动的Activity以至于可以resumed栈顶的Activity |
1 | void startSpecificActivityLocked(ActivityRecord r, |
因为我们要启动的MainActvity的进程还没有启动,所以会先调用startProcess来启一个新进程,参数是用户ID和进程名称,然后再通知这个应用程序进程将该Activity组件启动起来
1 | final ProcessRecord startProcessLocked(String processName, |
调用完Process。start之后还得向消息队列里面发送一个消息PROC_START_TIMEOUT_MSG,等待新创建的应用程序进程返回消息,这里设置了TIMEOUT,也就是必须在指定时间内完成调用
1 | //向线程队列中发送一个PROC_START_TIMEOUT_MSG,新的应用程序进程必须在PROC_START_TIMEOUT毫秒内完成启动工作 |
现在就来看看应用进程启动的过程,这个代码在ActivityThread中
ApplicationThread端 main
Process.start里指定的类就是ActivityThread,所以这里入口点就是main
1 | final ApplicationThread mAppThread = new ApplicationThread(); |
这个函数就是创建ActivityThread,以及创建一个消息循环,在thread.attach中调用AMS的attachApplication,把这个新建进程的ApplicationThread代理传递过去
1 | private void attach(boolean system) { |
然后又来到AMS端
AMS端 attachApplication
1 |
|
来到attachApplicationLocked,调用realStartActivityLocked启动要启动的顶端Activity组件
1 | boolean attachApplicationLocked(ProcessRecord app) throws RemoteException { |
realStartActivityLocked
1 | final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, |
这样流程又回到ApplicationThread
ApplicationThread端scheduleActivity
1 | // we use token to identify this activity without having to send the |
这个函数就是封装了一个ActivityClientRecord,然后发送一个LAUNCHER_ACTIITY的消息
处理逻辑在这里,每个App都是打包在一个apk文件里的,里面有Android的所有资源,ActivityThread里面用一个LoadedApk对象来描述已经加载的Apk文件
1 | public void handleMessage(Message msg) { |
1 | private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { |
仅需跟进performLaunchActivity,这个就是最核心的启动Activity的地方了,这里加载的Activity类,并完成了相应的初始化
1 | private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
到此根Activity就启动完毕了,可以看出其是连带着应用程序的启动的
进程内子Activity的启动
在Manifest中可以给Activity指定android:process属性,如果子Activity的process指定的和MainActivity一样,启动子Activity时,就会发现应用程序已经启动了,因此直接启动子Activity即可
启动子Activity的过程
- MainActivity向AMS发送启动子Activity的请求
- AMS保存子Activity信息,向MainActivity发送终止请求
- MainActivity进入中止状态后,再向AMS发送请求完成,AMS继续启动子Activity
- AMS发现子Activity的进程已经存在,就把要启动的子Activity信息直接发送给应用程序进程,让其启动子Activity
这其中大部分步骤都一样。
新进程中子Activity的启动
总结
进程实际是Linux底层的概念,其在Android Framwork层中的作用就是支撑实现,以及实现权限的划分,
Activity等组件才是Framwork层的基本组成部分,这就是常见的计算机中的分层的概念,而Task是一个更高级和抽象的概念,每一个Activity都是运行在一个Task中的,Task里整合了一系列相关的Activity组件,目的是共同完成一个业务功能。
在开发应用程序时,如果一个Activity—SomeActvity需要一个E-mail发送功能,就可以将Email中的EmailSender组件启动起来,运行在同一个任务中,等到Emai发送完毕后,再返回SomeActivity中,整个过程中用户根本不会看到SomeActivity和EmailSender是运行在两个不同的应用程序进程的。